/*
 * solenoid.cpp
 *
 *  Created on: 8-Jul-2009
 *      Author: nrqm
 */

#include "../arduino/WProgram.h"
#include "solenoid.h"

#define BASS_PWM_PIN 3			// PWM output pin for the bass drum.
#define MAIN_PWM_PIN 5			// PWM output pin for the main drum.
#define HIHAT_PWM_PIN 6			// PWM output pin for the hi-hat cymbal.

#define BASS_POT_PIN 0
#define MAIN_POT_PIN 2
#define HIHAT_POT_PIN 4

#define MAX_DUTY_CYCLE 0xFF

extern void print(const char* str);

extern uint16_t millis16();

Solenoid::Solenoid(DRUM_TYPE type) :
	type(type),
	duty_cycle(0),
	strike_start_time(0),
	strike_timeout(50)
{
	switch (type)
	{
	case BASS:
		max_duty_cycle = MAX_DUTY_CYCLE;
		note_divisor=0;
		pwm_pin = BASS_PWM_PIN;
		break;
	case MAIN:
		max_duty_cycle = MAX_DUTY_CYCLE;
		note_divisor=1;
		pwm_pin = MAIN_PWM_PIN;
		break;
	case HI_HAT:
		max_duty_cycle = MAX_DUTY_CYCLE;
		note_divisor=2;
		pwm_pin = HIHAT_PWM_PIN;
		break;
	default:
		break;
	}
	pinMode(pwm_pin, OUTPUT);
	analogWrite(pwm_pin, 0);
}

Solenoid* Solenoid::GetInstance(DRUM_TYPE type)
{
	static Solenoid bass = Solenoid(BASS);
	static Solenoid main = Solenoid(MAIN);
	static Solenoid hihat = Solenoid(HI_HAT);
	switch (type)
	{
	case BASS:
		return &bass;
	case MAIN:
		return &main;
	case HI_HAT:
		return &hihat;
	default:
		return NULL;
	}
}

void Solenoid::DoNoteOn(uint8_t note, uint8_t velocity)
{
	static const uint8_t scale_factor = 0xFF;	// this multiplies the velocity so that we can do precise integer division
	uint16_t velocity_ratio = 0;
	uint16_t scaled_duty_cycle = 0;
	if (note % 3 != note_divisor)
	{
		// This solenoid isn't listening for the note, so ignore the message.
		return;
	}
	if (velocity == 0)
	{
		// Some controllers send "note on" commands with velocity 0 in place of of "note off" commands.  We want to
		// ignore those so as not to terminate the strike prematurely when the note is release on such a device.
		return;
	}
	// Calculate the duty cycle that corresponds to the given velocity, by taking the ratio of the velocity
	// to the maximum velocity, and multiplying that by the maximum duty cycle.  The operations are done on scaled
	// 16-bit integers so that data aren't aliased by integer division.
	velocity_ratio = (velocity * scale_factor) / 0x7F;		// max velocity is 0x7F
	scaled_duty_cycle = (velocity_ratio * max_duty_cycle);
	duty_cycle = scaled_duty_cycle / scale_factor;

	// start timer for timeout
	strike_start_time = millis16();

	analogWrite(pwm_pin, duty_cycle);
}

void Solenoid::CheckSolenoid()
{
	// check to see if the PWM needs to be turned off.
	if (duty_cycle != 0 && (millis16() - strike_start_time > strike_timeout))
	{
		duty_cycle = 0;
		analogWrite(pwm_pin, duty_cycle);
	}
}

